﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Collections;
using System.Windows.Media;
using System.Collections.ObjectModel;
using System.Windows.Input;

namespace Sinacor.Infra.UI.Controls.SmartClient
{
    public class SinacorTreeView : TreeView, IItemsControlDragSource, IItemsControlDropTarget
    {
        public SinacorTreeView()
        {
            // BufferedEvent
            _bufferedSelectionChangedEventManager = new BufferedEventManager<RoutedPropertyChangedEventArgs<object>>();
            this.SelectedItemChanged += _bufferedSelectionChangedEventManager.EventCapturer;
            _bufferedSelectionChangedEventManager.AddBufferedEventListener(this, OnBufferedSelectionChanged);

            _selectedPath = new ObservableCollection<object>();
            this.SelectedPath = new ReadOnlyObservableCollection<object>(_selectedPath);

            this.AddHandler(SinacorTreeViewItem.SelectedEvent, new RoutedEventHandler(TreeViewItemSelected));
            this.AddHandler(SinacorTreeViewItem.UnselectedEvent, new RoutedEventHandler(TreeViewItemUnselected));

            DragDropManager dragDropManager = new DragDropManager(this);

            this.Loaded += new RoutedEventHandler(SinacorTreeView_Loaded);
            this.Unloaded += new RoutedEventHandler(SinacorTreeView_Unloaded);
        }

        #region Tratamento de eventos

        private void SinacorTreeView_Loaded(object sender, RoutedEventArgs e)
        {
            _bufferedSelectionChangedEventManager.Start();
        }
        private void SinacorTreeView_Unloaded(object sender, RoutedEventArgs e)
        {
            _bufferedSelectionChangedEventManager.Stop();
        }

        #endregion

        #region SelectedPath
        private ObservableCollection<object> _selectedPath;
        public ReadOnlyObservableCollection<object> SelectedPath { get; private set; }

        private void TreeViewItemSelected(object sender, RoutedEventArgs e)
        {
            _selectedPath.Clear();

            SinacorTreeViewItem treeViewItem = (SinacorTreeViewItem)e.OriginalSource;

            this.GetItem(treeViewItem);
        }

        private void GetItem(SinacorTreeViewItem treeViewItem)
        {
            object item = this.SelectedItem is SinacorTreeViewItem ? treeViewItem : treeViewItem.DataContext;

            if (item != null)
            {
                _selectedPath.Insert(0, item);

                SinacorTreeViewItem parent = ItemsControl.ItemsControlFromItemContainer(treeViewItem) as SinacorTreeViewItem;
                if (parent != null)
                    this.GetItem(parent);
            }
        }

        private void TreeViewItemUnselected(object sender, RoutedEventArgs e)
        {
            SinacorTreeViewItem item = (SinacorTreeViewItem)e.OriginalSource;

            if (this.SelectedItem == null)
            {
                _selectedPath.Clear();
            }
        }
        #endregion

        #region Selection

        #region ConfirmSelectionEvent
        public static readonly RoutedEvent ConfirmSelectionEvent = EventManager.RegisterRoutedEvent("ConfirmSelection", RoutingStrategy.Bubble, typeof(ItemEventHandler), typeof(SinacorTreeView));
        /// <summary>
        /// Evento disparado quando o usuário confima a seleção de um item
        /// </summary>
        public event ItemEventHandler ConfirmSelection
        {
            add { AddHandler(ConfirmSelectionEvent, value); }
            remove { RemoveHandler(ConfirmSelectionEvent, value); }
        }

        /// <summary>
        /// Dispara o evento <c>ConfirmSelection</c>
        /// </summary>
        /// <param name="element">Elemento que vai disparar o evento</param>
        /// <param name="index">Índice do item</param>
        /// <param name="item">Item</param>
        protected virtual void OnConfirmSelection(int index, object item)
        {
            ItemEventArgs e = new ItemEventArgs(ConfirmSelectionEvent, this, new ItemInfo(index, item));
            this.RaiseEvent(e);
        }
        #endregion

        #region BufferedSelectionChangedEvent
        private BufferedEventManager<RoutedPropertyChangedEventArgs<object>> _bufferedSelectionChangedEventManager;

        public static readonly RoutedEvent BufferedSelectionChangedEvent = EventManager.RegisterRoutedEvent("BufferedSelectionChanged", RoutingStrategy.Bubble, typeof(BufferedEventHandler<SelectionChangedEventArgs>), typeof(SinacorTreeView));
        /// <summary>
        /// Evento buferizado de SelectionChanged
        /// </summary>
        public event BufferedEventHandler<SelectionChangedEventArgs> BufferedSelectionChanged
        {
            add { AddHandler(BufferedSelectionChangedEvent, value); }
            remove { RemoveHandler(BufferedSelectionChangedEvent, value); }
        }

        protected virtual void OnBufferedSelectionChanged(object sender, RoutedPropertyChangedEventArgs<object> originalEventArgs, int eventCount)
        {
            List<object> removedItems = new List<object>();
            List<object> addedItems = new List<object>();

            if (originalEventArgs.OldValue != null)
                removedItems.Add(originalEventArgs.OldValue);

            if (originalEventArgs.NewValue != null)
                addedItems.Add(originalEventArgs.NewValue);

            SelectionChangedEventArgs selectionChangedEventArgs = new SelectionChangedEventArgs(ConfirmSelectionEvent, removedItems, addedItems);

            BufferedEventArgs<SelectionChangedEventArgs> e = new BufferedEventArgs<SelectionChangedEventArgs>(BufferedSelectionChangedEvent, selectionChangedEventArgs, eventCount);
            this.RaiseEvent(e);
        }
        #endregion

        #endregion

        #region DragDrop

        #region IItemsControlDragSource Members

        IList IItemsControlDragSource.GetDraggedItems()
        {
            return new List<object>() { this.SelectedItem };
        }

        #endregion

        #endregion

        protected override System.Windows.DependencyObject GetContainerForItemOverride()
        {
            return new SinacorTreeViewItem();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is SinacorTreeViewItem);
        }

        #region ToggleButton
        public static readonly DependencyProperty ToggleButtonStringFormatProperty = DependencyProperty.Register("ToggleButtonStringFormat", typeof(string), typeof(SinacorTreeView), new FrameworkPropertyMetadata(null));

        public string ToggleButtonStringFormat
        {
            get { return (string)GetValue(ToggleButtonStringFormatProperty); }
            set { SetValue(ToggleButtonStringFormatProperty, value); }
        }

        public static readonly DependencyProperty ToggleButtonTemplateProperty = DependencyProperty.Register("ToggleButtonTemplate", typeof(DataTemplate), typeof(SinacorTreeView), new FrameworkPropertyMetadata(null));

        public DataTemplate ToggleButtonTemplate
        {
            get { return (DataTemplate)GetValue(ToggleButtonTemplateProperty); }
            set { SetValue(ToggleButtonTemplateProperty, value); }
        }

        public static readonly DependencyProperty ToggleButtonTemplateSelectorProperty = DependencyProperty.Register("ToggleButtonTemplateSelector", typeof(DataTemplateSelector), typeof(SinacorTreeView), new FrameworkPropertyMetadata(null));

        public DataTemplateSelector ToggleButtonTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(ToggleButtonTemplateSelectorProperty); }
            set { SetValue(ToggleButtonTemplateSelectorProperty, value); }
        }
        #endregion
    }

    public class SinacorTreeViewItem : TreeViewItem, IItemContainerDragSource, IItemContainerDropTarget
    {
        public SinacorTreeViewItem()
        {
            //this.KeyDown += new KeyEventHandler(SinacorTreeViewItem_KeyDown);
            //this.MouseDoubleClick += new MouseButtonEventHandler(SinacorTreeViewItem_MouseDoubleClick);

            DragDropManager dragDropManager = new DragDropManager(this);
        }

        public int GetIndex()
        {
            ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(this);
            return itemsControl != null ? itemsControl.ItemContainerGenerator.IndexFromContainer(this) : -1;
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if (e.Key == Key.Enter)
            {
                this.OnConfirmSelection(this.GetIndex(), this.DataContext);
                e.Handled = true;
            }
        }

        protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
        {
            if (ControlHelper.FindParent((DependencyObject)e.Source, "PART_TreeViewItemContent") != null)
                this.OnConfirmSelection(this.GetIndex(), this.DataContext);

            base.OnMouseDoubleClick(e);
        }

        #region ConfirmSelection
        public static readonly RoutedEvent ConfirmSelectionEvent = SinacorTreeView.ConfirmSelectionEvent.AddOwner(typeof(SinacorTreeViewItem));
        /// <summary>
        /// Evento disparado quando o usuário confima a seleção de um item
        /// </summary>
        public event ItemEventHandler ConfirmSelection
        {
            add { AddHandler(ConfirmSelectionEvent, value); }
            remove { RemoveHandler(ConfirmSelectionEvent, value); }
        }

        /// <summary>
        /// Dispara o evento <c>ConfirmSelection</c>
        /// </summary>
        /// <param name="element">Elemento que vai disparar o evento</param>
        /// <param name="index">Índice do item</param>
        /// <param name="item">Item</param>
        protected virtual void OnConfirmSelection(int index, object item)
        {
            ItemEventArgs e = new ItemEventArgs(ConfirmSelectionEvent, this, new ItemInfo(index, item));
            this.RaiseEvent(e);
        }
        #endregion

        #region ToggleButton
        public static readonly DependencyProperty ToggleButtonProperty = DependencyProperty.Register("ToggleButton", typeof(object), typeof(SinacorTreeViewItem), new FrameworkPropertyMetadata(null));

        public object ToggleButton
        {
            get { return (object)GetValue(ToggleButtonProperty); }
            set { SetValue(ToggleButtonProperty, value); }
        }

        public static readonly DependencyProperty ToggleButtonStringFormatProperty = DependencyProperty.Register("ToggleButtonStringFormat", typeof(string), typeof(SinacorTreeViewItem), new FrameworkPropertyMetadata(null));

        public string ToggleButtonStringFormat
        {
            get { return (string)GetValue(ToggleButtonStringFormatProperty); }
            set { SetValue(ToggleButtonStringFormatProperty, value); }
        }

        public static readonly DependencyProperty ToggleButtonTemplateProperty = DependencyProperty.Register("ToggleButtonTemplate", typeof(DataTemplate), typeof(SinacorTreeViewItem), new FrameworkPropertyMetadata(null));

        public DataTemplate ToggleButtonTemplate
        {
            get { return (DataTemplate)GetValue(ToggleButtonTemplateProperty); }
            set { SetValue(ToggleButtonTemplateProperty, value); }
        }

        public static readonly DependencyProperty ToggleButtonTemplateSelectorProperty = DependencyProperty.Register("ToggleButtonTemplateSelector", typeof(DataTemplateSelector), typeof(SinacorTreeViewItem), new FrameworkPropertyMetadata(null));

        public DataTemplateSelector ToggleButtonTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(ToggleButtonTemplateSelectorProperty); }
            set { SetValue(ToggleButtonTemplateSelectorProperty, value); }
        }
        #endregion

        protected override System.Windows.DependencyObject GetContainerForItemOverride()
        {
            return new SinacorTreeViewItem();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is SinacorTreeViewItem);
        }

        #region IDragSource Members

        FrameworkElement IDragSource.GetDraggedContent()
        {
            return this.Template.FindName("PART_TreeViewItemContent", this) as FrameworkElement ?? this;
        }

        #endregion

        #region IItemContainerDragSource Members

        bool IItemContainerDragSource.IsDragging
        {
            get { return this.IsSelected; }
        }

        IItemsControlDragSource IItemContainerDragSource.GetItemsControl()
        {
            return ControlHelper.FindParent(this, typeof(SinacorTreeView)) as IItemsControlDragSource;
        }

        #endregion
    }
}